package com.linkhub.oms.admin.modules.user.bankstatement.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linkhub.oms.admin.modules.user.accountrecharge.domain.vo.UserRechargeRecordMatchVO;
import com.linkhub.oms.admin.modules.user.accountrecharge.service.IUserRechargeRecordService;
import com.linkhub.oms.admin.modules.user.bankstatement.domain.param.UserBankStatementQueryParam;
import com.linkhub.oms.admin.modules.user.bankstatement.domain.vo.UserBankStatementExcelVO;
import com.linkhub.oms.admin.modules.user.bankstatement.domain.vo.UserBankStatementVO;
import com.linkhub.oms.admin.modules.user.bankstatement.excel.dto.BankStatementExcelDataDTO;
import com.linkhub.oms.admin.modules.user.bankstatement.excel.listener.BankStatementExcelDataListener;
import com.linkhub.oms.admin.modules.user.bankstatement.service.IUserBankStatementService;
import com.linkhub.oms.common.constant.CacheKeyConstant;
import com.linkhub.oms.common.enums.mp.CurrencyTypeEnum;
import com.linkhub.oms.common.enums.mp.DataStatusEnum;
import com.linkhub.oms.common.enums.mp.MatchStatusEnum;
import com.linkhub.oms.common.response.PageDTO;
import com.linkhub.oms.common.util.QtBigDecimalUtil;
import com.linkhub.oms.common.util.QtOptionalUtil;
import com.linkhub.oms.common.util.QtStreamUtil;
import com.linkhub.oms.component.excel.util.EasyExcelUtil;
import com.linkhub.oms.component.redis.locker.DistributedLocker;
import com.linkhub.oms.dao.mapper.UserBankStatementEntityMapper;
import com.linkhub.oms.dao.mpservice.IBankCardService;
import com.linkhub.oms.dao.mpservice.IUserService;
import com.linkhub.oms.entity.BankCardEntity;
import com.linkhub.oms.entity.UserBankStatementEntity;
import com.linkhub.oms.entity.UserEntity;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;

/**
 * <p>
 * 用户银行流水 服务实现类
 * </p>
 *
 * @author Frank
 * @since 2024-05-26
 */
@Service
public class UserBankStatementServiceImpl extends ServiceImpl<UserBankStatementEntityMapper, UserBankStatementEntity> implements IUserBankStatementService {
    @Resource
    private IUserService userService;
    @Resource
    private IBankCardService bankCardService;
    @Resource
    private IUserRechargeRecordService userRechargeRecordService;
    @Resource
    private DistributedLocker distributedLocker;

    @Override
    public PageDTO<UserBankStatementVO> getBankStatementPage(UserBankStatementQueryParam param) {
        LambdaQueryWrapper<UserBankStatementEntity> queryWrapper = Wrappers.lambdaQuery(UserBankStatementEntity.class)
                .eq(param.isCustomRoleFlag(), UserBankStatementEntity::getUserId, param.getUserId())
                .eq(Objects.nonNull(param.getMatchStatus()), UserBankStatementEntity::getMatchStatus, param.getMatchStatus())
                .eq(param.isEmployeeRoleFlag(), UserBankStatementEntity::getMatchStatus, MatchStatusEnum.UN_MATCH)
                .eq(StringUtils.isNoneBlank(param.getUserCardNumber()), UserBankStatementEntity::getUserCardNumber, param.getUserCardNumber())
                .eq(StringUtils.isNoneBlank(param.getCompanyCardNumber()), UserBankStatementEntity::getCompanyCardNumber, param.getCompanyCardNumber())
                .eq(UserBankStatementEntity::getDataStatus, DataStatusEnum.ENABLE)
                .orderByDesc(UserBankStatementEntity::getReceivedDate);
        appendQueryDate(queryWrapper, param);

        Page<UserBankStatementEntity> objectPage = new Page<>(param.getPage(), param.getSize());
        Page<UserBankStatementEntity> page = super.page(objectPage, queryWrapper);

        PageDTO<UserBankStatementVO> userRechargeRecordPage = userBankStatementBuilder(page);
        setUserName(userRechargeRecordPage, param.isEmployeeRoleFlag());
        return userRechargeRecordPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserBankStatementExcelVO importExcel(String userId, MultipartFile file) {
        distributedLocker.lock(CacheKeyConstant.BANK_STATEMENT_IMPORT_LOCK_KEY);
        try {
            BankStatementExcelDataListener excelDataListener = new BankStatementExcelDataListener();
            List<BankStatementExcelDataDTO> list = EasyExcelUtil.read(file, BankStatementExcelDataDTO.class, excelDataListener);
            int failTotal = CollUtil.size(excelDataListener.acquireInValidList());
            if (CollUtil.isEmpty(list)) {
                return UserBankStatementExcelVO.failResult(failTotal);
            }

            // 去重
            List<BankStatementExcelDataDTO> pendingSaveList = bankStatementRepeatFilter(list,
                    BankStatementExcelDataDTO.FIELD_USER_CARD_NUMBER,
                    BankStatementExcelDataDTO.FIELD_RECEIVED_BANK_STATEMENT);
            //过滤
            pendingSaveList = otherFilter(pendingSaveList);

            int dataSize = CollUtil.size(list);
            int saveSize = CollUtil.size(pendingSaveList);
            int repeatSize = dataSize - saveSize;
            if (CollUtil.isEmpty(pendingSaveList)) {
                return UserBankStatementExcelVO.successResult(failTotal + repeatSize, saveSize);
            }
            //收款银行流水号（必填）
            List<String> receivedBankStatements = QtStreamUtil.filterAndMapAndDistinct(pendingSaveList,
                    pendingSave->StringUtils.isNotBlank(pendingSave.getReceivedBankStatement()),
                    BankStatementExcelDataDTO::getReceivedBankStatement);

            //删除重复的未匹配的银行流水(客户银行卡账号+到账银行流水号(都必填))
            List<UserBankStatementEntity> unMatchBankStatements = getUnMatchBankStatements(receivedBankStatements);
            deleteRepeatUnMatchBankStatements(pendingSaveList, unMatchBankStatements);

            // convert
            //银行卡匹配用户
            Map<String, String> cardNumberUserIdMap = getCardNumberUserMap(pendingSaveList);
            //已到账的充值记录
            List<UserRechargeRecordMatchVO> userRechargeRecordList = userRechargeRecordService.listByReceivedBankStatement(receivedBankStatements);
            Map<String, List<UserRechargeRecordMatchVO>> bankStatementRechargesMap = QtStreamUtil.group(userRechargeRecordList,
                    UserRechargeRecordMatchVO::getReceivedBankStatement);
            List<UserBankStatementEntity> userBankStatementEntities = QtStreamUtil.map(pendingSaveList, dto -> convert(userId,
                    dto,
                    cardNumberUserIdMap,
                    bankStatementRechargesMap));
            saveBatch(userBankStatementEntities);
            return UserBankStatementExcelVO.successResult(failTotal + repeatSize, saveSize);
        } finally {
            distributedLocker.unlock(CacheKeyConstant.BANK_STATEMENT_IMPORT_LOCK_KEY);
        }
    }

    /**
     * 过滤
     */
    private List<BankStatementExcelDataDTO> otherFilter(List<BankStatementExcelDataDTO> pendingSaveList) {
        return QtStreamUtil.filter(pendingSaveList, excelData -> {
            BigDecimal transferAmount = QtBigDecimalUtil.valueOf(excelData.getTransferAmount());
            if (!QtBigDecimalUtil.checkAmountValid(transferAmount)) {
                return false;
            }
            BigDecimal receivedAmount = QtBigDecimalUtil.valueOf(excelData.getReceivedAmount());
            if (!QtBigDecimalUtil.checkAmountValid(receivedAmount)) {
                return false;
            }
            BigDecimal fee = QtBigDecimalUtil.valueOf(excelData.getFee());
            if (!QtBigDecimalUtil.checkAmountValid(fee)) {
                return false;
            }
            //转账金额<=到账金额
            if (QtBigDecimalUtil.gt(receivedAmount, transferAmount)) {
                return false;
            }
            return true;
        });
    }

    /**
     * 获取映射 userCardNumber -> userId
     */
    private Map<String, String> getCardNumberUserMap(List<BankStatementExcelDataDTO> pendingSaveList) {
        List<String> userCardNumbers = QtStreamUtil.mapAndDistinct(pendingSaveList, BankStatementExcelDataDTO::getUserCardNumber);
        List<BankCardEntity> bankCardEntities = bankCardService.listByCardNumber(userCardNumbers);
        return QtStreamUtil.toMap(bankCardEntities, BankCardEntity::getCardNumber, BankCardEntity::getUserId);
    }

    /**
     * 删除重复的未匹配的银行流水(客户银行卡账号+到账银行流水号(都必填))
     *
     * @param pendingSaveList       本次导入
     * @param unMatchBankStatements 历史导入未匹配的银行流水
     */
    private void deleteRepeatUnMatchBankStatements(List<BankStatementExcelDataDTO> pendingSaveList,
                                                   List<UserBankStatementEntity> unMatchBankStatements) {
        if (CollUtil.isEmpty(unMatchBankStatements)) {
            return;
        }
        List<UserBankStatementEntity> deleteList = Lists.newArrayList();
        Map<String, List<BankStatementExcelDataDTO>> bankStatementExcelDataMap = QtStreamUtil.group(pendingSaveList,
                BankStatementExcelDataDTO::getReceivedBankStatement);
        Map<String, List<UserBankStatementEntity>> bankStatementEntitiesMap = QtStreamUtil.group(unMatchBankStatements,
                UserBankStatementEntity::getReceivedBankStatement);
        bankStatementEntitiesMap.forEach((bankStatement, bankStatementEntities) -> {
            List<BankStatementExcelDataDTO> bankStatementExcelDataList = bankStatementExcelDataMap.get(bankStatement);
            List<String> userCardList = QtStreamUtil.mapAndDistinct(bankStatementExcelDataList, BankStatementExcelDataDTO::getUserCardNumber);
            bankStatementEntities.forEach(bankStatementEntity -> {
                if (userCardList.contains(bankStatementEntity.getUserCardNumber())) {
                    bankStatementEntity.setDataStatus(DataStatusEnum.DELETE);
                    deleteList.add(bankStatementEntity);
                }
            });

        });
        QtOptionalUtil.of(deleteList).ifPresent(this::updateBatchById);
    }


    private List<UserBankStatementEntity> getUnMatchBankStatements(List<String> receivedBankStatements) {
        if (CollUtil.isEmpty(receivedBankStatements)) {
            return Lists.newArrayList();
        }
        return lambdaQuery()
                .select(UserBankStatementEntity::getId,
                        UserBankStatementEntity::getUserCardNumber,
                        UserBankStatementEntity::getReceivedBankStatement)
                .in(UserBankStatementEntity::getReceivedBankStatement, receivedBankStatements)
                .eq(UserBankStatementEntity::getMatchStatus, MatchStatusEnum.UN_MATCH)
                .eq(UserBankStatementEntity::getDataStatus, DataStatusEnum.ENABLE)
                .list();
    }

    /**
     * 去重（用户银行卡号+到账流水号）
     *
     * @param list   list
     * @param fields 字段名（public）
     */
    private static List<BankStatementExcelDataDTO> bankStatementRepeatFilter(List<BankStatementExcelDataDTO> list, String... fields) {
        Map<String, BankStatementExcelDataDTO> map = new LinkedHashMap<>();
        for (BankStatementExcelDataDTO obj : list) {
            StringBuilder keyBuilder = new StringBuilder();
            for (String field : fields) {
                try {
                    keyBuilder.append(obj.getClass().getDeclaredField(field).get(obj));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            String key = keyBuilder.toString();
            if (!map.containsKey(key)) {
                map.put(key, obj);
            }
        }
        return new ArrayList<>(map.values());
    }

    /**
     * 设置查询时间
     */
    private void appendQueryDate(LambdaQueryWrapper<UserBankStatementEntity> queryWrapper, UserBankStatementQueryParam param) {
        LocalDate dateBegin;
        LocalDate dateEnd;
        if (StringUtils.isBlank(param.getReceivedDateBegin()) || StringUtils.isBlank(param.getReceivedDateEnd())) {
            //默认查询1个月
            dateBegin = LocalDate.now().minusDays(30);
            dateEnd = LocalDate.now();
        } else {
            dateBegin = LocalDate.parse(param.getReceivedDateBegin());
            dateEnd = LocalDate.parse(param.getReceivedDateEnd());
        }
        queryWrapper.le(UserBankStatementEntity::getReceivedDate, dateEnd)
                .ge(UserBankStatementEntity::getReceivedDate, dateBegin);
    }


    /**
     * 构建PageDTO<UserBankStatementVO>
     */
    private PageDTO<UserBankStatementVO> userBankStatementBuilder(Page<UserBankStatementEntity> page) {
        PageDTO<UserBankStatementVO> res = new PageDTO<>();
        res.setTotal(page.getTotal());
        res.setSize(page.getSize());
        res.setCurrent(page.getCurrent());
        res.setPages(page.getPages());
        List<UserBankStatementVO> userBankStatements = QtStreamUtil.map(page.getRecords(), UserBankStatementVO::new);
        res.setRecords(userBankStatements);
        return res;
    }


    /**
     * 设置商户名称
     */
    private void setUserName(PageDTO<UserBankStatementVO> userRechargeRecordPage, boolean employeeRoleFlag) {
        List<UserBankStatementVO> records = userRechargeRecordPage.getRecords();
        if (employeeRoleFlag && CollUtil.isNotEmpty(records)) {
            List<String> userIds = QtStreamUtil.filterAndMapAndDistinct(records,
                    record -> StringUtils.isNotBlank(record.getMerchantUserId()),
                    UserBankStatementVO::getMerchantUserId);
            List<UserEntity> userEntities = userService.listByUserId(userIds);
            if (CollUtil.isEmpty(userEntities)) {
                return;
            }
            Map<String, UserEntity> userIdUserMap = QtStreamUtil.toMap(userEntities, UserEntity::getUserId);
            records.forEach(record -> {
                UserEntity userEntity = userIdUserMap.get(record.getMerchantUserId());
                if (Objects.nonNull(userEntity)) {
                    record.setMerchantUserName(userEntity.getUserName());
                    record.setMerchantUserEmail(userEntity.getEmail());
                }
            });
        }
    }

    /**
     * 转换UserBankStatementEntity
     *
     * @param userId                    userId
     * @param excelData                 导入数据集
     * @param cardNumberUserIdMap       map<cardNumber,userId>
     * @param bankStatementRechargesMap map<bankStatement,userRechargeRecords>
     * @return UserBankStatementEntity
     */
    private UserBankStatementEntity convert(String userId,
                                            BankStatementExcelDataDTO excelData,
                                            Map<String, String> cardNumberUserIdMap,
                                            Map<String, List<UserRechargeRecordMatchVO>> bankStatementRechargesMap) {
        UserBankStatementEntity userBankStatementEntity = new UserBankStatementEntity();
        String userCardNumber = excelData.getUserCardNumber();
        userBankStatementEntity.setUserId(cardNumberUserIdMap.get(userCardNumber));
        userBankStatementEntity.setUserCardNumber(userCardNumber);
        userBankStatementEntity.setCompanyCardNumber(excelData.getCompanyCardNumber());

        BigDecimal transferAmount = QtBigDecimalUtil.valueOf(excelData.getTransferAmount());
        if (Objects.nonNull(transferAmount)) {
            userBankStatementEntity.setTransferAmount(transferAmount.setScale(2, RoundingMode.DOWN));
        }

        CurrencyTypeEnum receivedCurrencyType = CurrencyTypeEnum.getByType(excelData.getReceivedCurrencyType());
        if (Objects.nonNull(receivedCurrencyType)) {
            userBankStatementEntity.setReceivedCurrencyType(receivedCurrencyType.getTypeStr());
        }
        BigDecimal receivedAmount = QtBigDecimalUtil.valueOf(excelData.getReceivedAmount());
        if (Objects.nonNull(receivedAmount)) {
            userBankStatementEntity.setReceivedAmount(receivedAmount.setScale(2, RoundingMode.DOWN));
        }

        CurrencyTypeEnum feeCurrencyType = CurrencyTypeEnum.getByType(excelData.getFeeCurrencyType());
        if (Objects.nonNull(feeCurrencyType)) {
            userBankStatementEntity.setFeeCurrencyType(feeCurrencyType.getTypeStr());
        }
        BigDecimal fee = QtBigDecimalUtil.valueOf(excelData.getFee());
        if (Objects.nonNull(fee)) {
            userBankStatementEntity.setFee(fee.setScale(2, RoundingMode.DOWN));
        }

        userBankStatementEntity.setReceivedDate(excelData.getReceivedDate());
        userBankStatementEntity.setReceivedBankStatement(excelData.getReceivedBankStatement());

        //用户银行卡号+收款流水号（已到账） -> 已匹配
        List<UserRechargeRecordMatchVO> userRechargeRecordMatchList = bankStatementRechargesMap.get(excelData.getReceivedBankStatement());
        UserRechargeRecordMatchVO userRechargeRecordMatch = QtStreamUtil.findFirst(userRechargeRecordMatchList,
                userRechargeRecordMatchVO -> Objects.equals(userCardNumber, userRechargeRecordMatchVO.getPayCardNumber()));
        if (Objects.nonNull(userRechargeRecordMatch)) {
            userBankStatementEntity.setTransactionId(userRechargeRecordMatch.getTransactionId());
            userBankStatementEntity.setMatchStatus(MatchStatusEnum.MATCHED);
        } else {
            userBankStatementEntity.setMatchStatus(MatchStatusEnum.UN_MATCH);
        }
        userBankStatementEntity.setCreatedUserId(userId);
        userBankStatementEntity.setUpdateUserId(userId);
        return userBankStatementEntity;
    }


}
